/**
 * @file minor.c
 * @author wu
 * @brief 小端机
 * @version 0.1
 * @date 2025-7-22
 *
 *
 **/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "shmem.h"
#include "msg_queue_peer.h"
#include <pthread.h>
#include "cJSON.h"

#define _PORT_ 8080
#define _BUF_MAX_ 1024

typedef enum
{
  false,
  true
} bool;
// 线程相关变量
pthread_t thread_recv;
pthread_t thread_send;

// 共享内存相关变量
struct shm_param para;
struct node_data *point;

int flag_type0 = 0; // 刷新上报标志位

int size;
union val_t
{
  bool bval;
  int ival;
  float fval;
};

struct node_data // 共享内存数据存储格式
{
  int key;
  int type;
  int dev_type;
  union val_t old_val;
  union val_t new_val;
  int ret;
};

struct setdata // 消息队列数据格式
{
  long devtype;
  int key;
  int type;
  union val_t val;
};

struct setdata b = {};
void msg_recv()
{
  while (1)
  {
    msg_queue_recv("msg", &b, sizeof(struct setdata), 3, 0);
    printf("MSGTYPE:STM32\n");

    if (b.type == 1)
      printf("msg_queue_recv:%ld %d %d %d\n", b.devtype, b.key, b.type, b.val.bval);
    else if (b.type == 2)
      printf("msg_queue_recv:%ld %d %d %d\n", b.devtype, b.key, b.type, b.val.ival);
    else
      printf("msg_queue_recv:%ld %d %d %.2f\n", b.devtype, b.key, b.type, b.val.fval);

    for (int i = 0; i < *p; i++)
    {
      if (n_data[i].key == b.key)
      {
        printf("recv:n_data:%d  key:%d\n", n_data[i].key, b.key);
        if (n_data[i].type == 1)
        {
          n_data[i].old_val.bval = n_data[i].new_val.bval;
          n_data[i].new_val.bval = b.val.bval;
          printf("old:%d new:%d\n", n_data[i].old_val.bval, n_data[i].new_val.bval);
        }
        else if (n_data[i].type == 2)
        {
          n_data[i].old_val.ival = n_data[i].new_val.ival;
          n_data[i].new_val.ival = b.val.ival;
          printf("old:%d new:%d\n", n_data[i].old_val.ival, n_data[i].new_val.ival);
        }
        else
        {
          n_data[i].old_val.fval = n_data[i].new_val.fval;
          n_data[i].new_val.fval = b.val.fval;
          printf("old:%.2f new:%.2f\n", n_data[i].old_val.fval, n_data[i].new_val.fval);
        }
      }
    }
  }
  pthread_exit("Minor no recv");
}

/**
 * @brief 将数据序列化为 JSON 字符串
 *
 * @param json 要操作的 JSON 对象指针（传入前需已初始化）
 * @param key 要添加的键名
 * @param stringArr 字符串数组指针（与number参数互斥）
 * @param number 数字数组指针（与stringArr参数互斥）
 * @param type 操作类型标志位：
 *             cleanall: 清空JSON对象
 *             cleanitem：清空指定对象
 *             pair: 添加键值对
 *             array: 添加数组
 *        支持连用，例如：“cleanall pair”；
 * @param size 数组元素个数（仅数组模式有效）
 * @return char* 序列化后的JSON字符串指针，需调用cJSON_free释放
 *
 * @note 函数执行流程：
 * 1. 按type标志清空JSON对象（bit0置位时）
 * 2. 根据type标志选择添加键值对（bit1）或数组（bit2）
 * 3. 字符串和数字类型通过参数指针非空判断自动选择
 * 4. 最终返回格式化后的JSON字符串
 */
void cjson_serialize(cJSON *json, const char *key, const char *string, const char *(*stringArr), const uint32_t *number, const char *type, const int size)
{
  // 清空JSON对象
  if (strncmp(type, "cleanall", 15))
  {
    cJSON_Delete(json);
    json = cJSON_CreateObject();
  }
  if (strncmp(type, "cleanitem", 15))
  {
    cJSON_Delete(cJSON_GetObjectItem(json, key));
  }
  // 添加键值对模式
  if (strncmp(type, "pair", 15))
  {
    // 字符串类型处理
    if (stringArr != NULL)
    {
      cJSON_AddItemToObject(json, key, cJSON_CreateString(*stringArr));
    }
    // 数字类型处理
    else
    {
      cJSON_AddItemToObject(json, key, cJSON_CreateNumber(*number));
    }
  }

  // 添加数组模式
  if (strncmp(type, "array", 15))
  {
    cJSON *arr = cJSON_CreateArray();
    // 字符串数组处理
    if (stringArr != NULL)
    {
      for (int i = 0; i < size; i++)
      {
        cJSON_AddItemToArray(arr, cJSON_CreateString(stringArr[i]));
      }
    }
    // 数字数组处理
    if (number != NULL)
      for (int i = 0; i < size; i++)
      {
        cJSON_AddItemToArray(arr, cJSON_CreateNumber(number[i]));
      }
    cJSON_AddItemToObject(json, key, arr);
  }
  // 清除
  if (json == NULL)
  {
    json = cJSON_CreateObject();
  }
}
// 转换json
void json_to_struct(cJSON *json, struct node_data *data)
{
  switch (json->string)
  {
  case "LED1":
    data->key = 303;
    data->type = 1;
    if (json->valuestring == "ON")
    {
      data->new_val.bval = 1;
    }
    else
    {
      data->new_val.bval = 0;
    }
    break;
  case "LED3":
    data->key = 305;
    data->type = 1;
    if (json->valuestring == "ON")
    {
      data->new_val.bval = 1;
    }
    else
    {
      data->type = 0;
    }
    break;
  case "Fan":
    data->key = 306;
    data->type = 1;
    if (json->valuestring == "ON")
    {
      data->new_val.bval = 1;
    }
    else
    {
      data->type = 0;
    }
    break;
  case "Buzzer":
    data->key = 304;
    data->type = 1;
    if (json->valuestring == "ON")
    {
      data->type = 1;
    }
    else
    {
      data->type = 0;
    }
    break;
  case "Vibrator":
    data->key = 307;
    data->type = 1;
    if (json->valuestring == "ON")
    {
      data->new_val.bval = 1;
      data->type = 1;
    }
    else
    {
      data->type = 0;
    }
    break;
  case "T":
    data->key = 301;
    data->type = 3;
    data->new_val.fval = json->valuedouble;
    break;
  case "H":
    data->key = 302;
    data->type = 3;
    data->new_val.fval = json->valuedouble;
    break;
  default:
    break;
  }
}
char *commands[] = {
    "LED1_ON",
    "LED1_OFF",
    "LED3_ON",
    "LED3_OFF",
    "Fan_ON",
    "Fan_OFF",
    "Buzzer_ON",
    "Buzzer_OFF",
    "Vibrator_ON",
    "Vibrator_OFF",
    "Fire_ON",
    "Fire_OFF"};

extern int sockfd;
void MinorSend()
{
  cJSON *send_json = cJSON_CreateObject();
  while (1)
  {
    if (b.devtype == 3)
    {
      switch (b.key)
      {
      case 303:
        if (b.val.bval)
          cjson_serialize(send_json, "cmd", commands[0], NULL, NULL, "clean array", 1);
        else
          cjson_serialize(send_json, "cmd", commands[1], NULL, NULL, "clean array", 1);
        break;
      case 305:
        if (b.val.bval)
          cjson_serialize(send_json, "cmd", commands[2], NULL, NULL, "clean array", 1);
        else
          cjson_serialize(send_json, "cmd", commands[3], NULL, NULL, "clean array", 1);
        break;
      case 306:
        if (b.val.bval)
          cjson_serialize(send_json, "cmd", commands[4], NULL, NULL, "clean array", 1);
        else
          cjson_serialize(send_json, "cmd", commands[5], NULL, NULL, "clean array", 1);
        break;
      case 304:
        if (b.val.bval)
          cjson_serialize(send_json, "cmd", commands[6], NULL, NULL, "clean array", 1);
        else
          cjson_serialize(send_json, "cmd", commands[7], NULL, NULL, "clean array", 1);
        break;
      default:
        break;
      }
      char *sj_ch = malloc(1024);
      sj_ch = cJSON_Print(send_json);
      send(sockfd, sj_ch, sizeof(sj_ch), 0);
    }
  }
}

//============================================================

char *devname[] = {"LED1", "LED3", "Fan", "T", "H", "Buzzer", "Vibrator"};
void cjson_deserialize(cJSON *json, struct node_data *data)
{
  for (int i = 0; i < sizeof(devname); i++)
  {
    json_to_struct(cJSON_GetObjectItem(json, devname[i]), data);
  }
}

int main(int argc, char const *argv[])
{
  int ret;
  int acceptfd;

  // 初始化共享内存
  shm_init(&para, "home", 1024);
  // 获取共享内存地址
  point = (struct node_data *)shm_getaddr(&para);
  // 1.创建套接字（socket）
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0)
  {
    perror("sock err");
    return -1;
  }
  printf("sockfd:%d\n", sockfd);
  // 2.指定网络信息
  struct sockaddr_in saddr, caddr;
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(atoi(_PORT_));
  saddr.sin_addr.s_addr = INADDR_ANY; // 本机IP
  int len = sizeof(caddr);
  // 3.绑定套接字（bind）
  if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
  {
    perror("bind err");
    return -1;
  }
  printf("bind okk\n");
  // 4.监听（listen）:将主动套接字变为被动套接字
  if (listen(sockfd, 6) < 0)
  // 队列1:(未连接队列)  4 5 6
  // 队列2:(已连接队列) 3
  {
    perror("listen err");
    return -1;
  }
  printf("listen okk\n");

  pthread_create(&thread_recv, NULL, msg_recv, NULL);
  pthread_create(&thread_send, NULL, MinorSend, NULL);
  // 5.接收客户端连接请求（accept）
  // 阻塞等待客户端连接
  while (1)
  {
    //&1
    acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
    // 在tcp服务器中,会有两类文件描述符
    // 1.socket函数的返回值:一个  用于连接
    // 2.accept函数的返回值:一个或多个  用于通信
    if (acceptfd < 0)
    {
      perror("accept err");
      return -1;
    }
    printf("acceptfd:%d\n", acceptfd);
    printf("ip:%s port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
    // 6.接收发送消息（send recv）
    char buf[_BUF_MAX_] = {0};

    while (1)
    {
      ret = recv(acceptfd, buf, sizeof(buf), 0);
      cjson_deserialize(cJSON_Parse(buf), point);
      if (ret < 0)
      {
        perror("recv err");
        return -1;
      }
      // 当客户端退出,recv返回值为0
      else if (ret == 0)
        break;
      printf("recv:%s\n", buf);
      memset(buf, 0, sizeof(buf)); // 清空
    }
    // 7.关闭套接字（close）
    close(acceptfd);
  }
  pthread_detach(thread_recv);
  close(sockfd);
  return 0;
}
